package com.wonderful.wedding.service;

import com.google.common.base.Optional;
import com.qcloud.sign.FileCloudSign;
import com.wonderful.wedding.client.MyCloudClient;
import com.wonderful.wedding.model.Candidate;
import com.wonderful.wedding.model.Group;
import com.wonderful.wedding.model.MyCloudResult;
import com.wonderful.wedding.model.Person;
import com.wonderful.wedding.repository.GroupPersonMappingRepository;
import com.wonderful.wedding.repository.GroupRepository;
import com.wonderful.wedding.repository.PersonRepository;
import org.json.JSONArray;
import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import static com.google.common.collect.Lists.newArrayList;

@Service
public class FaceService {
    @Value("${services.tencent.baseUrl}")
    private String baseUrl;

    @Value("${services.tencent.host}")
    private String host;

    @Value("${services.tencent.app.id}")
    private int appId;

    @Value("${services.tencent.secret.id}")
    private String secretId;

    @Value("${services.tencent.secret.key}")
    private String secretKey;

    @Value("${services.tencent.user.id}")
    private String userId;

    @Value("${services.tencent.bucket}")
    private String bucket;

    @Autowired
    private GroupPersonMappingRepository groupPersonMappingRepository;

    @Autowired
    private GroupRepository groupRepository;

    @Autowired
    private PersonRepository personRepository;

    public MyCloudResult createPersonWithImage(String personName, String groupName, File imageFile) throws IOException {
        Optional<Group> group = groupRepository.findGroupByName(groupName);
        if (group.isPresent()) {
            Optional<Person> person = personRepository.findPersonByName(personName);
            if (!person.isPresent()) {
                person = Optional.fromNullable(new Person(personName));
                personRepository.create(person.get());
            }

            return createPersonWithImage(group.get().getId(), person.get().getId(), imageFile);
        } else {
            return new MyCloudResult(Integer.MIN_VALUE, "group isn't exist!");
        }
    }

    public MyCloudResult createPersonWithImage(int personId, int groupId, File imageFile) throws IOException {
        MyCloudClient myCloudClient = new MyCloudClient();

        HashMap<String, String> header = generateHeader();

        HashMap<String, Object> body = generateBaseBody();
        body.put("person_id", personId);

        if (existInGroup(personId, groupId)) {
            return myCloudClient.addFace(baseUrl + "/face/addface", header, body, imageFile);
        } else {
            body.put("group_ids[0]", groupId);
            MyCloudResult result = myCloudClient.createPerson(baseUrl + "/face/newperson", header, body, imageFile);
            if (result.getCode() == 0) {
                groupPersonMappingRepository.create(groupId, personId);
            }
            return result;
        }
    }

    public Optional<Person> identify(File imageFile, String groupName) throws IOException {

        Optional<Group> group = groupRepository.findGroupByName(groupName);
        if (group.isPresent()) {
            MyCloudClient myCloudClient = new MyCloudClient();
            HashMap<String, String> header = generateHeader();

            HashMap<String, Object> body = generateBaseBody();
            body.put("group_id", group.get().getId());

            MyCloudResult myCloudResult = myCloudClient.createPerson(baseUrl + "/face/identify", header, body, imageFile);

            if (myCloudResult.getCode() == 0) {
                List<Candidate> candidates = generateIdentifyResult(myCloudResult.getData());
                if (candidates.size() > 0) {
                    Person person = groupPersonMappingRepository.findPersonByGroupAndPerson(group.get().getId(),
                        candidates.get(0).getPersonId());

                    person.setImages(newArrayList(imageFile));
                    return Optional.fromNullable(person);
                } else {
                    return Optional.fromNullable(null);
                }
            } else {
                return Optional.fromNullable(null);
            }

        } else {
            return Optional.fromNullable(null);
        }
    }

    private HashMap<String, Object> generateBaseBody() {
        HashMap<String, Object> body = new HashMap<>();
        body.put("appid", appId);
        body.put("bucket", bucket);
        return body;
    }

    private HashMap<String, String> generateHeader() {
        HashMap<String, String> header = new HashMap<>();
        header.put("Authorization", FileCloudSign.appSignV2(appId, secretId, secretKey, bucket, System.currentTimeMillis() / 1000 + 3600));
        header.put("Host", host);

        return header;
    }

    private List<Candidate> generateIdentifyResult(JSONObject data) {
        ArrayList<Candidate> candidates = new ArrayList<>();

        JSONArray responseDataJSONArray = data.getJSONArray("candidates");
        for (int i = 0; i < responseDataJSONArray.length(); i++) {
            JSONObject cadidate = responseDataJSONArray.getJSONObject(i);
            candidates.add(new Candidate(cadidate.getInt("person_id"),
                cadidate.getString("face_id"), cadidate.getDouble("confidence")));
        }

        return candidates;
    }


    public boolean existInGroup(int groupId, int personId) {
        return null != groupPersonMappingRepository.findPersonByGroupAndPerson(groupId, personId);
    }
}
